package com.panfeng.xcloud.boss.provider.assets.service.exchange.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binance.client.RequestOptions;
import com.binance.client.SyncRequestClient;
import com.binance.client.model.enums.*;
import com.binance.client.model.market.Candlestick;
import com.binance.client.model.trade.*;
import com.panfeng.xcloud.boss.provider.assets.dao.mapper.UserExchangePwdMapperExt;
import com.panfeng.xcloud.boss.provider.assets.service.exchange.IExchangeMethodService;
import com.panfeng.xcloud.boss.provider.assets.utils.OrderNoGenerate;
import com.panfeng.xcloud.boss.provider.dto.request.OperateBotReqDTO;
import com.panfeng.xcloud.boss.provider.dto.request.QuantBotDetailDTO;
import com.panfeng.xcloud.boss.provider.dto.request.SendEmailReqDTO;
import com.panfeng.xcloud.boss.provider.rpc.member.UserInfoFeignApi;
import com.panfeng.xcloud.common.core.candle.CandleEntry;
import com.panfeng.xcloud.common.core.candle.MyCandleEntry;
import com.panfeng.xcloud.common.core.configuration.DataDictionaryConfig;
import com.panfeng.xcloud.common.core.enums.*;
import com.panfeng.xcloud.common.core.exceptions.BaseBizException;
import com.panfeng.xcloud.common.core.utils.DES3Util;
import com.panfeng.xcloud.common.core.utils.KeyContactUtil;
import com.panfeng.xcloud.common.core.utils.SignatureUtils;
import com.panfeng.xcloud.common.core.web.vo.InnerTransferVO;
import com.panfeng.xcloud.common.core.web.vo.OrderDetailResponseVO;
import com.panfeng.xcloud.common.core.web.vo.TradeResponseVO;
import com.panfeng.xcloud.dao.member.entity.UserExchangePwd;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.*;

@Service
@Slf4j
public class BinanceMethodService implements IExchangeMethodService {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private DataDictionaryConfig dataDictionaryConfig;

    @Autowired
    private UserExchangePwdMapperExt userExchangePwdMapperExt;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserInfoFeignApi userInfoFeignApi;

    @Autowired
    @Qualifier(value = "notLoadBalancedRestTemplate")
    private RestTemplate restTemplate;

    @Value("${xdcloud.des3.secret-key}")
    private String secrectKey;

    /**
     * 查询账户总可用余额
     *
     * @param userId
     * @param valuationAsset
     * @return
     */
    /*@Override
    public BigDecimal getTotalAvaliableMoney(String userId, String valuationAsset) {
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        List<AccountBalanceV2> balances = syncRequestClient.getBalanceV2();
        for (AccountBalanceV2 balance : balances) {
            if (balance.getAsset().equals(CoinTypeEnum.USDT.getCode())) {
                return new BigDecimal(balance.getAvailableBalance());
            }
        }
        return BigDecimal.ZERO;
    }*/
    @Override
    public BigDecimal getTotalAvaliableMoney(String userId, String valuationAsset) {
        String path = "/fapi/v2/balance";
        String params = "";
        String apiResult = getApiResult(userId, path, params, HttpMethod.GET);
        List<AccountBalanceV2> balances = JSONArray.parseArray(apiResult, AccountBalanceV2.class);
        for (AccountBalanceV2 balance : balances) {
            if (balance.getAsset().equals(CoinTypeEnum.USDT.getCode())) {
                return new BigDecimal(balance.getAvailableBalance());
            }
        }
        return BigDecimal.ZERO;
    }

    /**
     * 查询某交易对保证金率
     *
     * @param userId
     * @param contractCode
     * @param direction
     * @return
     */
    /*@Override
    public BigDecimal getRiskRate(String userId, String contractCode, String direction) {
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        AccountInformationV2 accountInformation = syncRequestClient.getAccountInformationV2();

        //维持保证金
        BigDecimal totalMaintMargin = accountInformation.getTotalMaintMargin();

        //保证金余额
        BigDecimal totalMarginBalance = accountInformation.getTotalMarginBalance();

        //保证金比率
        BigDecimal riskRate = BigDecimal.ONE.subtract(totalMaintMargin.divide(totalMarginBalance, 6, BigDecimal.ROUND_DOWN));

        return riskRate;
    }*/
    @Override
    public BigDecimal getRiskRate(String userId, String contractCode, String direction) {
        String path = "/fapi/v2/account";
        String params = "";
        String apiResult = getApiResult(userId, path, params, HttpMethod.GET);

        AccountInformationV2 accountInformation = JSONObject.parseObject(apiResult, AccountInformationV2.class);

        //维持保证金
        BigDecimal totalMaintMargin = accountInformation.getTotalMaintMargin();

        //保证金余额
        BigDecimal totalMarginBalance = accountInformation.getTotalMarginBalance();

        //保证金比率
        BigDecimal riskRate = BigDecimal.ONE.subtract(totalMaintMargin.divide(totalMarginBalance, 6, BigDecimal.ROUND_DOWN));

        return riskRate;
    }

    /**
     * 查询某交易对的可用仓位
     *
     * @param userId
     * @param contractCode
     * @param direction
     * @param leverRate
     * @return
     */
    /*@Override
    public BigDecimal getAvaliablePosition(String userId, String contractCode, String direction, Integer leverRate) {
        direction = direction.equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG.toString() : PositionSide.SHORT.toString();
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        List<PositionRiskV2> positionRisks = syncRequestClient.getPositionRiskV2(contractCode);
        PositionRiskV2 positionRisk = new PositionRiskV2();
        for (PositionRiskV2 risk : positionRisks) {
            if (risk.getPositionSide().equals(direction)) {
                positionRisk = risk;
                break;
            }
        }

        BigDecimal avaliablePosition = new BigDecimal(positionRisk.getPositionAmt()).abs();
        return avaliablePosition;
    }*/
    @Override
    public BigDecimal getAvaliablePosition(String userId, String contractCode, String direction, Integer leverRate) {
        direction = direction.equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG.toString() : PositionSide.SHORT.toString();
        String path = "/fapi/v2/positionRisk";
        String params = "symbol=" + contractCode + "&";
        String apiResult = getApiResult(userId, path, params, HttpMethod.GET);
        List<PositionRiskV2> positionRisks = JSONArray.parseArray(apiResult, PositionRiskV2.class);
        PositionRiskV2 positionRisk = new PositionRiskV2();
        for (PositionRiskV2 risk : positionRisks) {
            if (risk.getPositionSide().equals(direction)) {
                positionRisk = risk;
                break;
            }
        }

        BigDecimal avaliablePosition = new BigDecimal(positionRisk.getPositionAmt()).abs();
        return avaliablePosition;
    }

    @Override
    public BigDecimal getPositionAgvPrice(String userId, String contractCode, String direction, Integer leverRate) {
        direction = direction.equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG.toString() : PositionSide.SHORT.toString();
        String path = "/fapi/v2/positionRisk";
        String params = "symbol=" + contractCode + "&";
        String apiResult = getApiResult(userId, path, params, HttpMethod.GET);
        List<PositionRiskV2> positionRisks = JSONArray.parseArray(apiResult, PositionRiskV2.class);
        PositionRiskV2 positionRisk = new PositionRiskV2();
        for (PositionRiskV2 risk : positionRisks) {
            if (risk.getPositionSide().equals(direction)) {
                positionRisk = risk;
                break;
            }
        }

        BigDecimal avgPrice = new BigDecimal(positionRisk.getEntryPrice()).abs();
        return avgPrice;
    }

    /**
     * 查询某交易对的可用余额
     *
     * @param userId
     * @param contractCode
     * @return
     */
    /*@Override
    public BigDecimal getAvaliableMoney(String userId, String contractCode) {
        //由于币安默认为全仓,故某交易对的可用余额其实就是总的可用余额
        BigDecimal avaliableMoney = getTotalAvaliableMoney(userId, ValuationAssetTypeEnum.USD.getCode());
        return avaliableMoney;
    }*/
    @Override
    public BigDecimal getAvaliableMoney(String userId, String contractCode) {
        //由于币安默认为全仓,故某交易对的可用余额其实就是总的可用余额
        BigDecimal avaliableMoney = getTotalAvaliableMoney(userId, ValuationAssetTypeEnum.USD.getCode());
        return avaliableMoney;
    }

    /**
     * 查询某交易对的可开数
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param direction
     * @param leverRate
     * @return
     */
    /*@Override
    public BigDecimal getAvaliablePieces(String userId, String destinationCoin, String sourceCoin, String direction, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        //该交易对可用余额
        BigDecimal availableMoney = getAvaliableMoney(userId, contractCode).multiply(new BigDecimal(leverRate));
        return availableMoney;
    }*/
    @Override
    public BigDecimal getAvaliablePieces(String userId, String destinationCoin, String sourceCoin, String direction, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        //该交易对可用余额
        BigDecimal availableMoney = getAvaliableMoney(userId, contractCode).multiply(new BigDecimal(leverRate));
        return availableMoney;
    }


    /**
     * 交易所下市价单
     *
     * @param operateBotReqDTO
     * @return
     */
    /*@Override
    public TradeResponseVO trade(OperateBotReqDTO operateBotReqDTO) {
        TradeResponseVO tradeResponseVO = new TradeResponseVO();
        SyncRequestClient syncRequestClient = getSyncRequestClient(operateBotReqDTO.getUserId());
        String contractCode = KeyContactUtil.getContractCode(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        PositionSide positionSide = operateBotReqDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG : PositionSide.SHORT;

        Order order = null;
        String newClientOrderId = OrderNoGenerate.getOrderIdByTime(operateBotReqDTO.getUserId());
        try {
            //平仓
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.SELL.getCode()) {
                if (positionSide == PositionSide.LONG) {
                    //平多
                    order = syncRequestClient.postOrder(contractCode, OrderSide.SELL, PositionSide.LONG,
                            OrderType.MARKET, null, String.valueOf(operateBotReqDTO.getAmount()), null, null, null, null, null, NewOrderRespType.RESULT);
                } else {
                    //平空
                    order = syncRequestClient.postOrder(contractCode, OrderSide.BUY, PositionSide.SHORT,
                            OrderType.MARKET, null, String.valueOf(operateBotReqDTO.getAmount()), null, null, null, null, null, NewOrderRespType.RESULT);
                }
            }

            //开仓
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.BUY.getCode()) {
                if (positionSide == PositionSide.LONG) {
                    //开多
                    order = syncRequestClient.postOrder(contractCode, OrderSide.BUY, PositionSide.LONG,
                            OrderType.MARKET, null, String.valueOf(operateBotReqDTO.getAmount()), null, null, newClientOrderId, null, null, NewOrderRespType.RESULT);
                } else {
                    //开空
                    order = syncRequestClient.postOrder(contractCode, OrderSide.SELL, PositionSide.SHORT,
                            OrderType.MARKET, null, String.valueOf(operateBotReqDTO.getAmount()), null, null, newClientOrderId, null, null, NewOrderRespType.RESULT);
                }
            }
        } catch (Exception e) {
            logger.error("超级量化trade下单时报错", e);
            String message = "超级量化trade下单时报错   用户id:" + operateBotReqDTO.getUserId()
                    + "   方向:" + operateBotReqDTO.getDirection()
                    + "   倍数:" + operateBotReqDTO.getLeverRate()
                    + "   交易对:" + contractCode
                    + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
            userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
            throw e;
        }

        tradeResponseVO.setCreatedAt(new Date().getTime());
        tradeResponseVO.setOrderId(order.getOrderId());
        tradeResponseVO.setNewClientOrderId(newClientOrderId);

        return tradeResponseVO;
    }*/
    @Override
    public TradeResponseVO trade(OperateBotReqDTO operateBotReqDTO) {
        if (operateBotReqDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BaseBizException(ResponseStatusEnum.TRADER_AMOUNT_CANT_SMALL_THAN_ZERO);
        }
        TradeResponseVO tradeResponseVO = new TradeResponseVO();
        String contractCode = KeyContactUtil.getContractCode(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        PositionSide positionSide = operateBotReqDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG : PositionSide.SHORT;
        String path = "/fapi/v1/order";
        Order order = null;
        BigDecimal oldAvaliablePosition = null;
        BigDecimal newAvaliablePosition = null;
        String newClientOrderId = "x-ESCR3cQx" + OrderNoGenerate.getOrderIdByTime(operateBotReqDTO.getUserId());

        try {
            //平仓
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.SELL.getCode()) {
                if (positionSide == PositionSide.LONG) {
                    //平多
                    String params = "symbol=" + contractCode + "&side=" + OrderSide.SELL + "&positionSide=" + PositionSide.LONG + "&type=" + OrderType.MARKET + "&quantity=" + operateBotReqDTO.getAmount() + "&newOrderRespType=" + NewOrderRespType.RESULT + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.POST);
                    order = JSONObject.parseObject(apiResult, Order.class);
                } else {
                    //平空
                    String params = "symbol=" + contractCode + "&side=" + OrderSide.BUY + "&positionSide=" + PositionSide.SHORT + "&type=" + OrderType.MARKET + "&quantity=" + operateBotReqDTO.getAmount() + "&newOrderRespType=" + NewOrderRespType.RESULT + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.POST);
                    order = JSONObject.parseObject(apiResult, Order.class);
                }
                tradeResponseVO.setOrderId(order.getOrderId());
            }
        } catch (Exception e) {
            logger.error("超级量化trade方法平仓时报错", e);
            String message = "超级量化trade平仓时报错   用户id:" + operateBotReqDTO.getUserId()
                    + "   方向:" + operateBotReqDTO.getDirection()
                    + "   倍数:" + operateBotReqDTO.getLeverRate()
                    + "   交易对:" + contractCode
                    + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
            userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
            throw e;
        }

        try {
            //开仓
            if (operateBotReqDTO.getOperateType() == OperateTypeEnum.BUY.getCode()) {
                oldAvaliablePosition = mustGetAvaliablePositionInTrade(operateBotReqDTO.getUserId(), contractCode, operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate());
                if (positionSide == PositionSide.LONG) {
                    //开多
                    String params = "symbol=" + contractCode + "&side=" + OrderSide.BUY + "&positionSide=" + PositionSide.LONG + "&type=" + OrderType.MARKET + "&quantity=" + operateBotReqDTO.getAmount() + "&newClientOrderId=" + newClientOrderId + "&newOrderRespType=" + NewOrderRespType.RESULT + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.POST);
                    order = JSONObject.parseObject(apiResult, Order.class);
                } else {
                    //开空
                    String params = "symbol=" + contractCode + "&side=" + OrderSide.SELL + "&positionSide=" + PositionSide.SHORT + "&type=" + OrderType.MARKET + "&quantity=" + operateBotReqDTO.getAmount() + "&newClientOrderId=" + newClientOrderId + "&newOrderRespType=" + NewOrderRespType.RESULT + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.POST);
                    order = JSONObject.parseObject(apiResult, Order.class);
                }
                tradeResponseVO.setNewClientOrderId(newClientOrderId);
            }
        } catch (Exception e) {
            logger.error("超级量化trade方法开仓时报错", e);
            newAvaliablePosition = mustGetAvaliablePositionInTrade(operateBotReqDTO.getUserId(), contractCode, operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate());
            if (oldAvaliablePosition.add(operateBotReqDTO.getAmount()).compareTo(newAvaliablePosition) == 0) {
                String message = "超级量化trade开仓时报错但实际交易所下单成功   用户id:" + operateBotReqDTO.getUserId()
                        + "   方向:" + operateBotReqDTO.getDirection()
                        + "   倍数:" + operateBotReqDTO.getLeverRate()
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
                tradeResponseVO.setNewClientOrderId(newClientOrderId);
            } else {
                String message = "超级量化trade开仓时报错实际交易所下单也失败   用户id:" + operateBotReqDTO.getUserId()
                        + "   方向:" + operateBotReqDTO.getDirection()
                        + "   倍数:" + operateBotReqDTO.getLeverRate()
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
                throw e;
            }
        }

        tradeResponseVO.setCreatedAt(new Date().getTime());

        return tradeResponseVO;
    }

    /**
     * 获取订单详情
     *
     * @param operateBotReqDTO
     * @param tradeResponseVO
     * @return
     */
    /*@Override
    public OrderDetailResponseVO getOrderDetail(OperateBotReqDTO operateBotReqDTO, TradeResponseVO tradeResponseVO) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        SyncRequestClient syncRequestClient = getSyncRequestClient(operateBotReqDTO.getUserId());
        //获取机器人key
        String userBotKey = KeyContactUtil.getUserBotKey(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate(),
                operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin(), operateBotReqDTO.getUserId());
        //获取机器人对象
        QuantBotDetailDTO quantBotDetailDTO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(userBotKey), QuantBotDetailDTO.class);
        PositionSide positionSide = operateBotReqDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG : PositionSide.SHORT;
        Order order = null;
        int symbol = 0;
        //获取订单详情1
        while (symbol < 5) {
            try {
                if (tradeResponseVO.getOrderId() == null || tradeResponseVO.getOrderId() == 0l) {
                    order = syncRequestClient.getOrder(contractCode, null, tradeResponseVO.getNewClientOrderId());
                } else {
                    order = syncRequestClient.getOrder(contractCode, tradeResponseVO.getOrderId(), null);
                }
                break;
            } catch (Exception e) {
                symbol++;

                if (symbol == 5) {
                    throw e;
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }

        //获取订单详情2
        *//*String feeAsset = "";
        BigDecimal fee = BigDecimal.ZERO;
        BigDecimal profit = BigDecimal.ZERO;

        List<MyTrade> trades = syncRequestClient.getAccountTrades(contractCode, tradeResponseVO.getCreatedAt(), null, null, 1000);
        for (MyTrade trade : trades) {
            if (new BigDecimal(order.getOrderId()).compareTo(new BigDecimal(trade.getOrderId())) == 0) {
                feeAsset = trade.getCommissionAsset();
                fee = fee.add(trade.getCommission().abs());
                profit = profit.add(trade.getRealizedPnl());
            }
        }*//*
        BigDecimal feeRate = new BigDecimal("0.0004");
        String feeAsset = CoinTypeEnum.USDT.getCode();
        BigDecimal fee = order.getCumQuote().multiply(feeRate);
        BigDecimal profit = BigDecimal.ZERO;
        //如果是平仓则需要计算盈利
        if (operateBotReqDTO.getOperateType() == OperateTypeEnum.SELL.getCode()) {
            if (positionSide == PositionSide.LONG) {
                //平多
                profit = (order.getAvgPrice().subtract(new BigDecimal(quantBotDetailDTO.getAvgPrice()))).multiply(order.getExecutedQty());
            } else {
                //平空
                profit = (new BigDecimal(quantBotDetailDTO.getAvgPrice()).subtract(order.getAvgPrice())).multiply(order.getExecutedQty());
            }
        }

        OrderDetailResponseVO orderDetailResponseVO = new OrderDetailResponseVO();
        orderDetailResponseVO.setContractCode(order.getSymbol());
        orderDetailResponseVO.setLeverRate(operateBotReqDTO.getLeverRate());
        orderDetailResponseVO.setDirection(operateBotReqDTO.getDirection());
        orderDetailResponseVO.setVolume(order.getOrigQty());
        orderDetailResponseVO.setVolumeNum(order.getOrigQty());
        orderDetailResponseVO.setPrice(order.getAvgPrice());
        orderDetailResponseVO.setCreatedAt(tradeResponseVO.getCreatedAt());
        orderDetailResponseVO.setFeeAsset(feeAsset);
        orderDetailResponseVO.setOrderId(order.getOrderId());
        orderDetailResponseVO.setNewClientOrderId(tradeResponseVO.getNewClientOrderId());
        orderDetailResponseVO.setTradeAvgPrice(order.getAvgPrice());
        orderDetailResponseVO.setTradeTurnover(order.getCumQuote());
        orderDetailResponseVO.setTradeVolume(order.getExecutedQty());
        orderDetailResponseVO.setTradeVolumeNum(order.getExecutedQty());
        orderDetailResponseVO.setRealProfit(profit);
        orderDetailResponseVO.setFee(fee);

        return orderDetailResponseVO;
    }*/
    @Override
    public OrderDetailResponseVO getOrderDetail(OperateBotReqDTO operateBotReqDTO, TradeResponseVO tradeResponseVO) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin());
        String path = "/fapi/v1/order";
        //获取机器人key
        String userBotKey = KeyContactUtil.getUserBotKey(operateBotReqDTO.getExchangeType(), operateBotReqDTO.getDirection(), operateBotReqDTO.getLeverRate(),
                operateBotReqDTO.getDestinationCoin(), operateBotReqDTO.getSourceCoin(), operateBotReqDTO.getUserId());
        //获取机器人对象
        QuantBotDetailDTO quantBotDetailDTO = JSONObject.parseObject(stringRedisTemplate.opsForValue().get(userBotKey), QuantBotDetailDTO.class);
        PositionSide positionSide = operateBotReqDTO.getDirection().equals(PositionSideTypeEnum.BUY.getCode()) ? PositionSide.LONG : PositionSide.SHORT;
        Order order = null;
        while (order == null) {
            try {
                //获取订单详情
                if (tradeResponseVO.getOrderId() == null || tradeResponseVO.getOrderId() == 0l) {
                    String params = "symbol=" + contractCode + "&origClientOrderId=" + tradeResponseVO.getNewClientOrderId() + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.GET);
                    order = JSONObject.parseObject(apiResult, Order.class);
                } else {
                    String params = "symbol=" + contractCode + "&orderId=" + tradeResponseVO.getOrderId() + "&";
                    String apiResult = getApiResult(operateBotReqDTO.getUserId(), path, params, HttpMethod.GET);
                    order = JSONObject.parseObject(apiResult, Order.class);
                }
            } catch (Exception e) {
                String message = "超级量化获取订单详情时报错返回值发信息   用户id:" + operateBotReqDTO.getUserId()
                        + "   方向:" + operateBotReqDTO.getDirection()
                        + "   倍数:" + operateBotReqDTO.getLeverRate()
                        + "   数量:" + operateBotReqDTO.getAmount()
                        + "   目的币种:" + operateBotReqDTO.getDestinationCoin()
                        + "   操作类型:" + operateBotReqDTO.getOperateType()
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }

        BigDecimal feeRate = new BigDecimal("0.0004");
        String feeAsset = CoinTypeEnum.USDT.getCode();
        BigDecimal fee = order.getCumQuote().multiply(feeRate);
        BigDecimal profit = BigDecimal.ZERO;
        //如果是平仓则需要计算盈利
        if (operateBotReqDTO.getOperateType() == OperateTypeEnum.SELL.getCode()) {
            if (positionSide == PositionSide.LONG) {
                //平多
                profit = (order.getAvgPrice().subtract(new BigDecimal(quantBotDetailDTO.getAvgPrice()))).multiply(order.getExecutedQty());
            } else {
                //平空
                profit = (new BigDecimal(quantBotDetailDTO.getAvgPrice()).subtract(order.getAvgPrice())).multiply(order.getExecutedQty());
            }
        }

        OrderDetailResponseVO orderDetailResponseVO = new OrderDetailResponseVO();
        orderDetailResponseVO.setContractCode(order.getSymbol());
        orderDetailResponseVO.setLeverRate(operateBotReqDTO.getLeverRate());
        orderDetailResponseVO.setDirection(operateBotReqDTO.getDirection());
        orderDetailResponseVO.setVolume(order.getOrigQty());
        orderDetailResponseVO.setVolumeNum(order.getOrigQty());
        orderDetailResponseVO.setPrice(order.getAvgPrice());
        orderDetailResponseVO.setCreatedAt(tradeResponseVO.getCreatedAt());
        orderDetailResponseVO.setFeeAsset(feeAsset);
        orderDetailResponseVO.setOrderId(order.getOrderId());
        orderDetailResponseVO.setNewClientOrderId(tradeResponseVO.getNewClientOrderId());
        orderDetailResponseVO.setTradeAvgPrice(order.getAvgPrice());
        orderDetailResponseVO.setTradeTurnover(order.getCumQuote());
        orderDetailResponseVO.setTradeVolume(order.getExecutedQty());
        orderDetailResponseVO.setTradeVolumeNum(order.getExecutedQty());
        orderDetailResponseVO.setRealProfit(profit);
        orderDetailResponseVO.setFee(fee);

        return orderDetailResponseVO;
    }

    /**
     * 同账户内资金划转
     *
     * @param innerTransferVO
     * @return
     */
    @Override
    public String transferInner(InnerTransferVO innerTransferVO) {
        SyncRequestClient syncRequestClient = getSyncRequestClient(innerTransferVO.getUserId());
        Long tranId = syncRequestClient.innerTransfer(innerTransferVO.getAsset(), innerTransferVO.getAmount(), 2, null, new Date().getTime());
        return String.valueOf(tranId);
    }

    /**
     * 获取展示市价
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @return
     */
    /*@Override
    public BigDecimal getAppearMarketPrice(String userId, String destinationCoin, String sourceCoin) {
        int symbol = 0;
        while (symbol < 5) {
            try {
                String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
                if (StringUtils.isEmpty(userId)) {
                    userId = BlConstants.DEFAULT_USER_ID;
                }
                SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
                BigDecimal markPrice = syncRequestClient.getSymbolPriceTicker(contractCode).get(0).getPrice();
                redisTemplate.opsForValue().set(KeyContactUtil.getAppearMarketKey(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin), String.valueOf(markPrice));
                return markPrice;
            } catch (ApiException e) {
                symbol++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }
        throw new BaseBizException(ResponseStatusEnum.GET_APPERPRICE_ERROR);
    }*/
    @Override
    public BigDecimal getAppearMarketPrice(String userId, String destinationCoin, String sourceCoin) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.binance_rest_host.getCode());
        String url = restHost + "/fapi/v1/ticker/price?symbol=" + contractCode;
        JSONObject reqData = restTemplate.getForObject(url, JSONObject.class);
        BigDecimal markPrice = new BigDecimal(reqData.getString("price"));
        redisTemplate.opsForValue().set(KeyContactUtil.getAppearMarketKey(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin), String.valueOf(markPrice));
        return markPrice;
    }

    /**
     * 获取行情深度,供计算使用
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @return
     */
    /*@Override
    public JSONObject getMarketPriceDepth(String userId, String destinationCoin, String sourceCoin) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        try {
            JSONObject depth = syncRequestClient.getOrderBook(contractCode, null);
            return depth;
        } catch (Exception e) {
            throw new BaseBizException(ResponseStatusEnum.MARKET_PRICE_REQUEST_ERROR);
        }
    }*/
    @Override
    public JSONObject getMarketPriceDepth(String userId, String destinationCoin, String sourceCoin) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        try {
            String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.binance_rest_host.getCode());
            String url = restHost + "/fapi/v1/depth?symbol=" + contractCode;
            JSONObject depth = restTemplate.getForObject(url, JSONObject.class);
            return depth;
        } catch (Exception e) {
            throw new BaseBizException(ResponseStatusEnum.MARKET_PRICE_REQUEST_ERROR);
        }
    }

    /**
     * 判断杠杆倍数是否可用
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param leverRate
     * @return
     */
    @Override
    public boolean leverRateCanUse(String userId, String destinationCoin, String sourceCoin, Integer leverRate) {
        String[] split = {
                "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
                "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
                "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
                "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
                "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
                "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
                "70", "71", "72", "73", "74", "75", "100", "125"};
        return Arrays.asList(split).contains(String.valueOf(leverRate));
    }

    /**
     * 调用接口调整杠杆倍数
     *
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param leverRate
     * @return
     */
    /*@Override
    public boolean changeLeverRate(String userId, String destinationCoin, String sourceCoin, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        Leverage result = syncRequestClient.changeInitialLeverage(contractCode, leverRate);
        if (result.getLeverage().compareTo(new BigDecimal(leverRate)) == 0) {
            return true;
        } else {
            return false;
        }
    }*/
    @Override
    public boolean changeLeverRate(String userId, String destinationCoin, String sourceCoin, Integer leverRate) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        String path = "/fapi/v1/leverage";
        String params = "symbol=" + contractCode + "&leverage=" + leverRate + "&";
        String apiResult = getApiResult(userId, path, params, HttpMethod.POST);
        Leverage result = JSONObject.parseObject(apiResult, Leverage.class);
        if (result.getLeverage().compareTo(new BigDecimal(leverRate)) == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取K线数据
     *
     * @param exchangeType
     * @param userId
     * @param destinationCoin
     * @param sourceCoin
     * @param interval
     * @param startTime
     * @param endTime
     * @param limit
     * @return
     */
    /*@Override
    public JSONObject getCandleStick(Integer exchangeType, String userId, String destinationCoin, String sourceCoin, CandlestickInterval interval, Long startTime, Long endTime, int limit) {
        SyncRequestClient syncRequestClient = getSyncRequestClient(userId);
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);
        List<Candlestick> candles = syncRequestClient.getCandlestick(contractCode, interval, startTime, endTime, limit);
        List<CandleEntry> entries = new ArrayList<>();
        for (int i = 0; i < candles.size(); i++) {
            Candlestick candlestick = candles.get(i);
            entries.add(new MyCandleEntry(i, candlestick.getHigh().floatValue(), candlestick.getLow().floatValue(), candlestick.getOpen().floatValue(), candlestick.getClose().floatValue(), candlestick.getVolume().floatValue()));
        }
        JSONArray result = JSONArray.parseArray(JSONArray.toJSONString(entries));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("candlesticks", result);

        return jsonObject;
    }*/
    @Override
    public JSONObject getCandleStick(Integer exchangeType, String userId, String destinationCoin, String sourceCoin, CandlestickInterval interval, Long startTime, Long endTime, int limit) {
        String contractCode = KeyContactUtil.getContractCode(ExchangeTypeEnum.BINANCE.getCode(), destinationCoin, sourceCoin);

        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.binance_rest_host.getCode());
        String url = restHost + "/fapi/v1/klines?symbol=" + contractCode + "&interval=" + interval.toString() + "&limit=" + limit;

        if (startTime != null) {
            url = url.concat("&startTime=" + startTime);
        }

        if (endTime != null) {
            url = url.concat("&endTime=" + endTime);
        }

        JSONArray reqData = restTemplate.getForObject(url, JSONArray.class);
        List<Candlestick> candles = packCandles(reqData);
        List<CandleEntry> entries = new ArrayList<>();
        for (int i = 0; i < candles.size(); i++) {
            Candlestick candlestick = candles.get(i);
            entries.add(new MyCandleEntry(i, candlestick.getHigh().floatValue(), candlestick.getLow().floatValue(), candlestick.getOpen().floatValue(), candlestick.getClose().floatValue(), candlestick.getVolume().floatValue(), candlestick.getOpenTime(), candlestick.getCloseTime(), candlestick.getQuoteAssetVolume()));
        }
        JSONArray result = JSONArray.parseArray(JSONArray.toJSONString(entries));
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("candlesticks", result);

        return jsonObject;
    }

    //---------------------------------------------------------------------分隔符-------------------------------------------------------------------------------------
    private SyncRequestClient getSyncRequestClient(String userId) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.binance_rest_host.getCode());
        RequestOptions requestOptions = new RequestOptions();
        requestOptions.setUrl(restHost);
        SyncRequestClient syncRequestClient = SyncRequestClient.create(apiKey, secretKey, requestOptions);
        return syncRequestClient;
    }

    public String getApiResult(String userId, String path, String params, HttpMethod httpMethod) {
        String[] apiKeyAndSecretKey = getKeys(userId);
        String secretKey = apiKeyAndSecretKey[0];
        String apiKey = apiKeyAndSecretKey[1];
        String restHost = dataDictionaryConfig.getCodeToNameMap().get(DataDictionaryTypeEnum.binance_rest_host.getCode());

        //请求头
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-MBX-APIKEY", apiKey);
        //封装请求头
        HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(headers);

        long recvWindow = 5000;
        long timestamp = restTemplate.getForObject(restHost + "/fapi/v1/time", JSONObject.class).getLong("serverTime");
        //拼装参数
        params = params + "recvWindow=" + recvWindow + "&timestamp=" + timestamp;

        //参数签名
        String signature = SignatureUtils.sign(params, secretKey);

        //参数加入签名
        params = params + "&signature=" + signature;

        //拼装路径
        String url = restHost + path + "?" + params;

        ResponseEntity<String> result = restTemplate.exchange(url, httpMethod, formEntity, String.class);
        System.out.println(result);
        return result.getBody();
    }

    private String[] getKeys(String userId) {
        String[] keys = {"", ""};
        List<UserExchangePwd> userExchangePwd = userExchangePwdMapperExt.queryByUserId(userId, ExchangeTypeEnum.BINANCE.getCode());

        if (userExchangePwd.size() != 2)
            throw new BaseBizException(ResponseStatusEnum.QUAN_BOT_BINANCE_API_KEY_NOT_BIND);

        //API KEY 解密
        for (UserExchangePwd userExchangePwdAsset : userExchangePwd) {
            if (userExchangePwdAsset.getExchangeType().equals(ExchangeTypeEnum.BINANCE.getCode())) {
                String salt = userExchangePwdAsset.getSalt();
                Integer pwdType = userExchangePwdAsset.getPwdType();
                String pwd = userExchangePwdAsset.getPwd();
                String decode = DES3Util.doubleDecode(pwd, secrectKey, salt);
                if (ExchangePwdTypeEnum.secretKey.code == pwdType) {
                    keys[0] = decode;
                } else if (ExchangePwdTypeEnum.apiKey.code == pwdType) {
                    keys[1] = decode;
                }
            }
        }
        return keys;
    }

    private List<Candlestick> packCandles(JSONArray reqData) {
        List<Candlestick> result = new LinkedList();
        for (Object reqDatum : reqData) {
            Candlestick element = new Candlestick();
            ArrayList<Object> data = (ArrayList) reqDatum;
            element.setOpenTime((Long) data.get(0));
            element.setOpen(new BigDecimal((String) data.get(1)));
            element.setHigh(new BigDecimal((String) data.get(2)));
            element.setLow(new BigDecimal((String) data.get(3)));
            element.setClose(new BigDecimal((String) data.get(4)));
            element.setVolume(new BigDecimal((String) data.get(5)));
            element.setCloseTime((Long) data.get(6));
            element.setQuoteAssetVolume(new BigDecimal((String) data.get(7)));
            element.setNumTrades((Integer) data.get(8));
            element.setTakerBuyBaseAssetVolume(new BigDecimal((String) data.get(9)));
            element.setTakerBuyQuoteAssetVolume(new BigDecimal((String) data.get(10)));
            element.setIgnore(new BigDecimal((String) data.get(11)));
            result.add(element);
        }
        return result;
    }

    private BigDecimal mustGetAvaliablePositionInTrade(String userId, String contractCode, String direction, Integer leverRate) {
        BigDecimal avaliablePosition = null;
        while (avaliablePosition == null) {
            try {
                avaliablePosition = getAvaliablePosition(userId, contractCode, direction, leverRate);
            } catch (Exception e) {
                logger.error("超级量化trade方法中获取仓位时报错", e);
                String message = "超级量化trade方法中获取仓位时报错  用户id:" + userId
                        + "   方向:" + direction
                        + "   倍数:" + leverRate
                        + "   交易对:" + contractCode
                        + "   异常信息:" + com.panfeng.xcloud.common.core.utils.StringUtils.printStackTraceToString(e);
                userInfoFeignApi.sendEmail(new SendEmailReqDTO(message));
            }
        }
        return avaliablePosition;
    }

}
